package config

import (
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/spf13/viper"
)

// Config holds all configuration for the application
type Config struct {
	Environment string         `mapstructure:"environment"`
	Server      ServerConfig   `mapstructure:"server"`
	Database    DatabaseConfig `mapstructure:"database"`
	Cache       RedisConfig    `mapstructure:"cache"`
	Storage     StorageConfig  `mapstructure:"storage"`
	JWT         JWTConfig      `mapstructure:"jwt"`
	Registry    RegistryConfig `mapstructure:"registry"`
}

// ServerConfig holds server configuration
type ServerConfig struct {
	Port         int `mapstructure:"port"`
	ReadTimeout  int `mapstructure:"read_timeout"`
	WriteTimeout int `mapstructure:"write_timeout"`
	IdleTimeout  int `mapstructure:"idle_timeout"`
}

// DatabaseConfig holds database configuration
type DatabaseConfig struct {
	Host     string `mapstructure:"host"`
	Port     int    `mapstructure:"port"`
	Name     string `mapstructure:"name"`
	User     string `mapstructure:"user"`
	Password string `mapstructure:"password"`
	SSLMode  string `mapstructure:"ssl_mode"`
}

// RedisConfig holds Redis configuration
type RedisConfig struct {
	Addr     string `mapstructure:"addr" default:"localhost:6379"`
	Password string `mapstructure:"password"`
	DB       int    `mapstructure:"db" default:"0"`
	PoolSize int    `mapstructure:"pool_size" default:"10"`
}

// StorageConfig holds object storage configuration
type StorageConfig struct {
	Type      string `mapstructure:"type"` // "minio" or "s3"
	Endpoint  string `mapstructure:"endpoint"`
	AccessKey string `mapstructure:"access_key"`
	SecretKey string `mapstructure:"secret_key"`
	Bucket    string `mapstructure:"bucket"`
	Region    string `mapstructure:"region"`
	UseSSL    bool   `mapstructure:"use_ssl"`
}

// JWTConfig holds JWT configuration
type JWTConfig struct {
	SecretKey     string        `mapstructure:"secret_key"`
	TokenDuration time.Duration `mapstructure:"token_duration" default:"24h"`
}

// RegistryConfig holds Docker registry configuration
type RegistryConfig struct {
	Realm   string `mapstructure:"realm"`
	Service string `mapstructure:"service"`
	Issuer  string `mapstructure:"issuer"`
}

// Load loads configuration from environment variables and config files
func Load() (*Config, error) {
	viper.SetConfigName("config")
	viper.SetConfigType("yaml")
	viper.AddConfigPath(".")
	viper.AddConfigPath("./configs")
	viper.AddConfigPath("/etc/car")

	// Set default values
	setDefaults()

	// Read from environment variables
	viper.AutomaticEnv()
	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

	// Try to read config file
	if err := viper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
			return nil, fmt.Errorf("failed to read config file: %w", err)
		}
		// Config file not found, continue with environment variables and defaults
	}

	// Override with environment variables
	overrideFromEnv()

	var config Config
	if err := viper.Unmarshal(&config); err != nil {
		return nil, fmt.Errorf("failed to unmarshal config: %w", err)
	}

	return &config, nil
}

// setDefaults sets default configuration values
func setDefaults() {
	viper.SetDefault("environment", "development")

	// Server defaults
	viper.SetDefault("server.port", 8080)
	viper.SetDefault("server.read_timeout", 30)
	viper.SetDefault("server.write_timeout", 30)
	viper.SetDefault("server.idle_timeout", 120)

	// Database defaults
	viper.SetDefault("database.host", "localhost")
	viper.SetDefault("database.port", 5432)
	viper.SetDefault("database.name", "car")
	viper.SetDefault("database.user", "car")
	viper.SetDefault("database.password", "car123")
	viper.SetDefault("database.ssl_mode", "disable")

	// Redis defaults
	viper.SetDefault("redis.host", "localhost")
	viper.SetDefault("redis.port", 6379)
	viper.SetDefault("redis.password", "")
	viper.SetDefault("redis.db", 0)

	// Storage defaults
	viper.SetDefault("storage.type", "minio")
	viper.SetDefault("storage.endpoint", "localhost:9000")
	viper.SetDefault("storage.access_key", "minioadmin")
	viper.SetDefault("storage.secret_key", "minioadmin123")
	viper.SetDefault("storage.bucket", "car")
	viper.SetDefault("storage.region", "us-east-1")
	viper.SetDefault("storage.use_ssl", false)

	// JWT defaults
	viper.SetDefault("jwt.secret", "your-secret-key")
	viper.SetDefault("jwt.expiration", 24) // 24 hours

	// Registry defaults
	viper.SetDefault("registry.realm", "CAR Registry")
	viper.SetDefault("registry.service", "car")
	viper.SetDefault("registry.issuer", "car-registry")
}

// overrideFromEnv overrides configuration with environment variables
func overrideFromEnv() {
	// Database
	if host := os.Getenv("DB_HOST"); host != "" {
		viper.Set("database.host", host)
	}
	if port := os.Getenv("DB_PORT"); port != "" {
		if p, err := strconv.Atoi(port); err == nil {
			viper.Set("database.port", p)
		}
	}
	if name := os.Getenv("DB_NAME"); name != "" {
		viper.Set("database.name", name)
	}
	if user := os.Getenv("DB_USER"); user != "" {
		viper.Set("database.user", user)
	}
	if password := os.Getenv("DB_PASSWORD"); password != "" {
		viper.Set("database.password", password)
	}

	// Redis
	if host := os.Getenv("REDIS_HOST"); host != "" {
		viper.Set("redis.host", host)
	}
	if port := os.Getenv("REDIS_PORT"); port != "" {
		if p, err := strconv.Atoi(port); err == nil {
			viper.Set("redis.port", p)
		}
	}
	if password := os.Getenv("REDIS_PASSWORD"); password != "" {
		viper.Set("redis.password", password)
	}

	// Storage
	if endpoint := os.Getenv("MINIO_ENDPOINT"); endpoint != "" {
		viper.Set("storage.endpoint", endpoint)
	}
	if accessKey := os.Getenv("MINIO_ACCESS_KEY"); accessKey != "" {
		viper.Set("storage.access_key", accessKey)
	}
	if secretKey := os.Getenv("MINIO_SECRET_KEY"); secretKey != "" {
		viper.Set("storage.secret_key", secretKey)
	}

	// JWT
	if secret := os.Getenv("JWT_SECRET"); secret != "" {
		viper.Set("jwt.secret", secret)
	}
}

// GetDSN returns the database connection string
func (c *DatabaseConfig) GetDSN() string {
	// If Name contains .db or sqlite, treat as SQLite file path
	if strings.HasSuffix(c.Name, ".db") || strings.Contains(c.Name, "sqlite") {
		return c.Name
	}
	// Otherwise, use PostgreSQL DSN format
	return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
		c.Host, c.Port, c.User, c.Password, c.Name, c.SSLMode)
}

// GetRedisAddr returns the Redis address
func (c *RedisConfig) GetRedisAddr() string {
	return c.Addr
}